package com.pisolution.scheduler.remote.tracker;

import com.pisolution.scheduler.remote.command.Command;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 用于追踪客户端发送的请求.
 *
 * @author sora
 * @version 0.1.0
 */
public final class RequestTracker {
    private static final Logger LOGGER = LoggerFactory.getLogger(RequestTracker.class);

    /** 请求存取介质. */
    private static final ConcurrentHashMap<Long, RequestTracker> TRACKER = new ConcurrentHashMap<>(256);

    // -------------------------------------------------------------------------------------------------

    /** 请求命令标识符. */
    private final long opaque;

    /** 处理请求的时间限制. */
    private final long timeout;

    // -------------------------------------------------------------------------------------------------

    /** 服务端对于请求的响应. */
    private Command response;

    /** 请求处理失败原因. */
    private Throwable cause;

    // -------------------------------------------------------------------------------------------------

    /** 用于阻塞当前线程以等待请求处理完成. */
    private final CountDownLatch latch;

    /** 请求开始处理的时间. */
    private final long beginTimestamp;

    /** 请求是否发送成功. */
    private volatile boolean isSentOk = true;

    // -------------------------------------------------------------------------------------------------
    //  初始化
    // -------------------------------------------------------------------------------------------------

    /**
     * 创建一个新的 {@link RequestTracker} 实例来处理给定的请求.
     *
     * @param opaque 待处理请求命令标识符.
     * @param timeout 请求处理的时间限制.
     */
    public RequestTracker(final long opaque, final long timeout) {
        this.opaque = opaque;
        this.timeout = timeout;

        this.latch = new CountDownLatch(1);
        this.beginTimestamp = System.currentTimeMillis();

        TRACKER.put(opaque, this);
    }

    // -------------------------------------------------------------------------------------------------
    //  属性访问
    // -------------------------------------------------------------------------------------------------

    public static RequestTracker getTracker(final long opaque) {
        return TRACKER.get(opaque);
    }

    public Throwable getCause() {
        return this.cause;
    }

    public void setCause(Throwable cause) {
        this.cause = cause;
    }

    public boolean isSentOk() {
        return this.isSentOk;
    }

    public void setSentOk(boolean sentOk) {
        isSentOk = sentOk;
    }

    // -------------------------------------------------------------------------------------------------
    //  辅助方法
    // -------------------------------------------------------------------------------------------------

    /**
     * 客户端收到服务端响应 从追踪器中移除追踪的请求.
     *
     * @param response 服务端反馈的响应信息.
     */
    public void putResponse(final Command response) {
        this.response = response;

        this.latch.countDown();
        TRACKER.remove(this.opaque);
    }

    /**
     * 等待服务端处理该请求并予以反馈.
     *
     * @return 服务端对于该请求的反馈.
     *
     * @throws InterruptedException 如果该请求被打断.
     */
    @SuppressWarnings("ResultOfMethodCallIgnored")
    public Command waitResponse() throws InterruptedException {
        this.latch.await(this.timeout, TimeUnit.MILLISECONDS);

        return this.response;
    }

    /**
     * 从追踪存储介质中移除超时的请求.
     */
    public static void removeTimeout() {
        // 1. 创建迭代器以读取请求存储介质.
        final Iterator<Map.Entry<Long, RequestTracker>> iterator = TRACKER.entrySet().iterator();

        // 2. 迭代存储介质.
        while (iterator.hasNext()) {
            final RequestTracker tracker = iterator.next().getValue();

            // 3. 确认请求是否过期.
            if ((tracker.beginTimestamp + tracker.timeout + 1000) <= System.currentTimeMillis()) {
                iterator.remove();

                LOGGER.warn("Remove the outdated request {}.", tracker);
            }
        }
    }
}
